package com.xyzwps.collections.stack;

import com.xyzwps.collections.Utils;

import java.util.Objects;

public class ArrayStack<E> implements Stack<E> {

    private int top = -1;
    private E[] data;

    public ArrayStack(int capacity) {
        checkCapacity(capacity);
        this.data = Utils.newArray(capacity);
    }

    private static final int MIN_CAPACITY = 1;

    private void checkCapacity(int capacity) {
        if (capacity < MIN_CAPACITY) {
            throw new IllegalArgumentException("Capacity CANNOT be less than " + MIN_CAPACITY + ".");
        }
    }

    private static final int DEFAULT_CAPACITY = Utils.ARRAY_BASED_COLLECTION_DEFAULT_CAPACITY;

    public ArrayStack() {
        this(DEFAULT_CAPACITY);
    }

    @Override
    public E push(E element) {
        if (this.isFull()) {
            this.data = Utils.enlarge(this.data);
        }
        this.data[++this.top] = element;
        return element;
    }

    private boolean isFull() {
        return this.top + 1 == this.data.length;
    }


    @Override
    public E pop() {
        checkEmpty();

        E element = this.peek();
        this.data[this.top] = null;
        this.top--;

        final int currentCapacity = this.data.length;
        if (this.size() > DEFAULT_CAPACITY && this.top < (currentCapacity >> 2)) {
            this.data = Utils.shrink(this.data, this.size());
        }

        return element;
    }

    @Override
    public E peek() {
        checkEmpty();
        return this.data[this.top];
    }

    private void checkEmpty() {
        if (this.isEmpty()) {
            throw new IllegalStateException("Stack is EMPTY.");
        }
    }

    @Override
    public int size() {
        return this.top + 1;
    }

    @Override
    public boolean isEmpty() {
        return this.size() == 0;
    }

    @Override
    public void clear() {
        this.top = -1;
        this.data = Utils.newArray(DEFAULT_CAPACITY);
    }

    @Override
    public boolean has(E element) {
        for (int i = 0; i <= this.top; i++) {
            if (Objects.equals(this.data[i], element)) return true;
        }
        return false;
    }
}
